package com.pactera.asmp.server.job.dispatch.impl.compete;

import com.alibaba.fastjson.JSONObject;
import com.pactera.asmp.server.common.constants.DeviceEventType;
import com.pactera.asmp.server.common.constants.OccupyStatus;
import com.pactera.asmp.server.common.constants.TaskEventType;
import com.pactera.asmp.server.common.constants.TaskStatus;
import com.pactera.asmp.server.common.logprint.utils.CollectionUtils;
import com.pactera.asmp.server.common.logprint.utils.StringUtils;
import com.pactera.asmp.server.entity.TaskDTO;
import com.pactera.asmp.server.entity.TaskDevice;
import com.pactera.asmp.server.job.dispatch.BaseDispatchBus;
import com.pactera.asmp.server.job.dispatch.IBaseEvent;
import com.pactera.asmp.server.job.dispatch.impl.AsmpTaskDispatchBus;
import com.pactera.asmp.server.job.dispatch.impl.BaseManager;
import com.pactera.asmp.server.job.dispatch.impl.feedback.FeedbackManager;
import com.pactera.asmp.server.job.dispatch.IDeviceEvent;
import com.pactera.asmp.server.job.dispatch.ITaskEvent;
import com.pactera.asmp.server.service.ScreenDataService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;

@Slf4j
@Component
public class CompeteManager extends BaseManager {

    @Autowired
    private CompeteDataRepository repository;

    @Autowired
    @Lazy
    private ScreenDataService screenDataService;

    @Autowired
    private FeedbackManager feedbackManager;

    @Override
    protected BaseDispatchBus initDispatcher() {
        return AsmpTaskDispatchBus.getDispatchBus();
    }

    @Override
    public void deviceEventHandler(IDeviceEvent event) {
        if (event.getType().equals(DeviceEventType.DEVICE_PREPARE_OCCUPY)) {
            handlePrepareOccupy(event);
        }
    }

    @Override
    public void taskHandler(ITaskEvent event) {
        if (event.getType().equals(TaskEventType.TASK_COMPLETE)) {
            handleTaskComplete(event);
        }
        if (event.getType().equals(TaskEventType.TASK_FAIL)) {
            handleTaskFail(event);
        }
        if (event.getType().equals(TaskEventType.TASK_CANCEL)){
            //任务取消
            handleTaskCancel(event);
        }
    }

    private void handlePrepareOccupy(IDeviceEvent event){
        if( ! event.getDataType().equals(TaskDevice.class)){
            log.error("type error.");
            //log.error
            return;
        }
        TaskDevice taskDevice = JSONObject.parseObject(event.getData(), TaskDevice.class);
        //校验事件，幂等。查一下是否状态一致
        TaskDevice taskDeviceDB = repository.getTaskDevice(taskDevice.getId());
        if(! taskDeviceDB.getOccupyStatus().equals(OccupyStatus.INIT.getValue())){
            //重复事件  log.info
            return;
        }
        log.info("排队或占据设备！taskId {}, duid {}, auxDuid {}.", taskDevice.getTaskId(), taskDevice.getDuid(), taskDevice.getAuxDuid());
        //排队或占据设备
        queueOrOccupy(taskDevice, event);
    }

    private void queueOrOccupy(TaskDevice taskDevice, IBaseEvent event){
        //查询排队状态的taskDevice
        List<TaskDevice> taskDeviceQueueList = repository.getTaskDeviceListByDeviceAndStatus(taskDevice.getDuid(), OccupyStatus.QUEUE_OCCUPY.getValue());
        List<TaskDevice> taskDeviceOccupyList = repository.getTaskDeviceListByDeviceAndStatus(taskDevice.getDuid(), OccupyStatus.OCCUPIED.getValue());
        //如果有排队，否则占据。 发相应事件
        if(CollectionUtils.isEmpty(taskDeviceQueueList) &&
                CollectionUtils.isEmpty(taskDeviceOccupyList)){
            //占据设备，(并发问题),加锁--防止争抢
            boolean isOccupy = repository.modifyOccupyStatus(taskDevice, OccupyStatus.OCCUPIED.getValue());
            if(isOccupy){
                log.info("成功占据设备！taskId {}, duid {}, auxDuid {}.", taskDevice.getTaskId(), taskDevice.getDuid(), taskDevice.getAuxDuid());
                sendTaskDeviceEvent(taskDevice, DeviceEventType.DEVICE_OCCUPY, event);

                //更新大屏
                TaskDTO task = repository.getTaskDTO(taskDevice.getTaskId().intValue());
                screenDataService.refreshTaskInfo(task.getUserId().toString(), task.getProjectId());
            }else {
                log.info("占据设备失败，重新抢占！taskId {}, duid {}, auxDuid {}.", taskDevice.getTaskId(), taskDevice.getDuid(), taskDevice.getAuxDuid());
                //重新发出事件去抢占
                sendTaskDeviceEvent(taskDevice, DeviceEventType.DEVICE_PREPARE_OCCUPY, event);
            }
            return ;
        }
        //排队状态更新
        repository.modifyOccupyStatus(taskDevice, OccupyStatus.QUEUE_OCCUPY.getValue());
        //排队事件发送
        sendTaskDeviceEvent(taskDevice, DeviceEventType.DEVICE_QUEUE_OCCUPY, event);
    }


    /**
     * 设备占据
     * @param duid 设备Id
     */
    private void occupy(String duid, IBaseEvent event){
        List<TaskDevice> occupyDeviceList = repository.getTaskDeviceListByDeviceAndStatus(duid, OccupyStatus.OCCUPIED.getValue());
        if(CollectionUtils.isNotEmpty(occupyDeviceList)){
            //已经被占了
            return;
        }
        //查询排队状态的taskDevice
        List<TaskDevice> taskDeviceList = repository.getTaskDeviceListByDeviceAndStatus(duid, OccupyStatus.QUEUE_OCCUPY.getValue());
        if(CollectionUtils.isEmpty(taskDeviceList)){
            repository.releaseDevice(duid);
            return;
        }
        TreeMap<Long, TaskDevice> treeMap = new TreeMap<>(new Comparator<Long>() {
            @Override
            public int compare(Long aLong, Long t1) {
                if(aLong < t1){
                    return -1;
                }
                return 1;
            }
        });
        for(TaskDevice taskDevice : taskDeviceList){
            TaskDTO taskDTO = repository.getTaskDTO(taskDevice.getTaskId().intValue());
            //通过treeMap, 按照执行开始时间排序
            treeMap.put(taskDTO.getExecTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(),taskDevice);
        }
        TaskDevice firstTaskDevice = treeMap.firstEntry().getValue();
        //占据设备，(并发问题),加锁--防止争抢
        boolean isOccupy = repository.modifyOccupyStatus(firstTaskDevice, OccupyStatus.OCCUPIED.getValue());
        if(isOccupy){
            //发送抢占成功事件
            sendTaskDeviceEvent(firstTaskDevice, DeviceEventType.DEVICE_OCCUPY, event);
        }else {
            //重新排队事件
            sendTaskDeviceEvent(firstTaskDevice, DeviceEventType.DEVICE_PREPARE_OCCUPY, event);
        }
    }

    private void handleTaskCancel(ITaskEvent event){
        //等设备取消任务
        this.sleep(1000L);
        releaseAndOccupy(Long.valueOf(event.getTaskId()), event);
    }



    private void handleTaskComplete(ITaskEvent event){
        TaskDTO task = JSONObject.parseObject(event.getData(), TaskDTO.class);
        releaseAndOccupy(Long.valueOf(event.getTaskId()), event);
        //更新大屏
        screenDataService.refreshTaskInfo(task.getUserId().toString(), task.getProjectId());

        this.sleep(2000L);
        repository.modifyTestReportCount(task);
        if(!task.getStatus().equals(TaskStatus.TASK_CANCEL.getValue())) {
            feedbackManager.feedbackForTaskSendEmail(task);
        }
    }

    private void handleTaskFail(ITaskEvent event){
        releaseAndOccupy(Long.valueOf(event.getTaskId()), event);
        TaskDTO task = JSONObject.parseObject(event.getData(), TaskDTO.class);
        //更新大屏
        screenDataService.refreshTaskInfo(task.getUserId().toString(), task.getProjectId());
    }

    /**
     * 解除，并发起抢占
     * @param taskId 任务ID
     */
    private void releaseAndOccupy(Long taskId, IBaseEvent event){
        List<TaskDevice> devices = releaseDevices( taskId);
        if(CollectionUtils.isNotEmpty(devices)){
            for(TaskDevice td: devices){
                occupy(td.getDuid(), event);
            }
        }
    }

    private List<TaskDevice> releaseDevices(Long taskId){
        List<TaskDevice> occupyList = new ArrayList<>();
        List<TaskDevice> list = repository.getTaskDeviceListById(taskId.intValue());
        if(CollectionUtils.isNotEmpty(list)) {
            for (TaskDevice taskDevice : list) {
                //如果占有设备
                if(taskDevice.getOccupyStatus().equals(OccupyStatus.OCCUPIED.getValue())) {
                    //给其他任务排队抢占
                    occupyList.add(taskDevice);
                }
                repository.modifyTaskDeviceStatus(taskDevice, OccupyStatus.RELEASED.getValue());
                if( ! StringUtils.isEmpty(taskDevice.getAuxDuid())){
                    //aux没有排队机制，所以释放与deviceInfo空闲
                    repository.releaseDevice(taskDevice.getAuxDuid());
                }
            }
        }
        return occupyList;
    }

    @Override
    public String getBussinessType() {
        return CompeteManager.class.getSimpleName();
    }
}
